package com.aurora.leetcode.multithreading;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author : qiaodan
 * @date : 2024/8/21 13:32
 * @description <p>1115. 交替打印 FooBar</p>
 * <p>给你一个类：
 * class FooBar {
 *   public void foo() {
 *     for (int i = 0; i < n; i++) {
 *       print("foo");
 *     }
 *   }
 *
 *   public void bar() {
 *     for (int i = 0; i < n; i++) {
 *       print("bar");
 *     }
 *   }
 * }
 * 两个不同的线程将会共用一个 FooBar 实例：
 *
 * 线程 A 将会调用 foo() 方法，而
 * 线程 B 将会调用 bar() 方法
 * 请设计修改程序，以确保 "foobar" 被输出 n 次。</p>

 * <p>示例 1：</p>
 *<p>
 * 输入：n = 1
 * 输出："foobar"
 * 解释：这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法，"foobar" 将被输出一次。</p>
 * <p>示例 2：</p>
 *<p>
 * 输入：n = 2
 * 输出："foobarfoobar"
 * 解释："foobar" 将被输出两次。</p>
 *
 *
 * 提示：
 *
 * 1 <= n <= 1000
 */
public class FooBar_1115 {
    AtomicInteger integer = new AtomicInteger(1);
    private int n;

    public FooBar_1115(int n) {
        this.n = n;
    }


    /**
     * 我的实现
     */
    public void foo1(Runnable printFoo) throws InterruptedException {
        synchronized (integer){
            for (int i = 0; i < n; i++) {
                if(integer.compareAndSet(1, 0)){
                    printFoo.run();
                    integer.notify();
                    integer.wait();
                }
            }
            integer.notify();
        }


    }

    public void bar1(Runnable printBar) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            synchronized (integer){
                if(integer.compareAndSet(0, 1)){
                    printBar.run();
                    integer.notify();
                    integer.wait();
                }
            }
        }

    }

    /**
     * 参考实现 1、使用阻塞队列
     */
    private BlockingQueue<Integer> fQueue = new LinkedBlockingQueue<>(1);
    private BlockingQueue<Integer> bQueue = new LinkedBlockingQueue<>(1);
    public void foo2(Runnable printFoo) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            fQueue.put(1);
            printFoo.run();
            bQueue.put(1);
        }
    }
    public void bar2(Runnable printBar) throws InterruptedException {
        for (int i=0; i< n; ++i){
            bQueue.take();
            printBar.run();
            fQueue.take();
        }
    }

    /**
     * 参考实现 2、使用CyclicBarrier
     * 共享变量需要添加volatile变量，否则线程将读取本地变量导致异常问题
     */
    CyclicBarrier cb = new CyclicBarrier(2);

    volatile boolean fin = true;
    public void foo3(Runnable printFoo) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            try {
                while(!fin);
                printFoo.run();
                fin = false;
                cb.await();
            } catch (BrokenBarrierException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public void bar3(Runnable printBar) throws InterruptedException {
        for (int i=0; i< n; ++i){
            try {
                cb.await();
                printBar.run();
                fin = true;
            } catch (BrokenBarrierException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 参考实现 3、可重入锁
     * @param printFoo
     * @throws InterruptedException
     */
    Lock lock = new ReentrantLock(true);
    Condition condition = lock.newCondition();
    volatile boolean flag = true;
    public void foo4(Runnable printFoo) throws InterruptedException {
        for (int i = 0; i < n; i++) {
                while(!flag);
                try{
                    lock.lock();
                    printFoo.run();
                    flag = false;
                    condition.signal();
                }finally {
                    lock.unlock();
                }
        }
    }

    public void bar4(Runnable printBar) throws InterruptedException {
        for (int i=0; i< n; ++i){
            try {
                lock.lock();
                if(flag){
                    condition.await();
                }
                printBar.run();
                flag = true;
            }finally {
                lock.unlock();
            }
        }
    }

    /**
     * 参考实现 4、信号量
     * @param printFoo
     * @throws InterruptedException
     */
    Semaphore foo = new Semaphore(1);
    Semaphore bar = new Semaphore(0);
    public void foo(Runnable printFoo) throws InterruptedException {
        for (int i = 0; i < n; i++) {
            foo.acquire();
            printFoo.run();
            bar.release();
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        for (int i=0; i< n; ++i){
            bar.acquire();
            printBar.run();
            foo.release();
        }
    }




    public static void main(String[] args) throws InterruptedException {
        Lock lock = new ReentrantLock(true);
        Condition condition = lock.newCondition();
        FooTask fooTask = new FooTask();
        BarTask barTask = new BarTask();
        FooBar_1115 fooBar1115 = new FooBar_1115(5);
        Thread thread1 = new Thread(() -> {
            try {
                fooBar1115.foo(fooTask);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                fooBar1115.bar(barTask);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        thread1.start();
        thread2.start();



    }
}
class FooTask implements Runnable{
    @Override
    public void run() {
        System.out.println("foo");
    }
}
class BarTask implements Runnable{

    @Override
    public void run() {
        System.out.println("bar");
    }
}
